package com.gitee.loyo

import com.fasterxml.jackson.databind.ObjectMapper
import com.gitee.loyo.act.remote.Mapper
import com.gitee.loyo.interfaces.TypeReference
import kotlin.reflect.KClass
import kotlin.reflect.full.isSubclassOf

class JacksonMapper(
    private val objectMapper: ObjectMapper
): Mapper {

    override fun <T : Any> serialize(model: T): String {
        return objectMapper.writeValueAsString(model)
    }

    override fun <T : Any> mapping(string: String, typeReference: TypeReference<T>): T {
        val javaType = objectMapper.typeFactory.constructType(typeReference.getType())
        return objectMapper.readValue(string, javaType) as T
    }

    override fun <T : Any> mappingList(string: String, typeReference: TypeReference<T>): List<T> {
        return when{
            typeReference.isParameterizedPair() -> {
                val javaType = objectMapper.typeFactory.constructType(LinkedHashMap::class.java)
                val collectionType = objectMapper.typeFactory.constructCollectionType(ArrayList::class.java, javaType)
                val mapList = objectMapper.readValue(string, collectionType) as List<LinkedHashMap<String, Any?>>
                val (typeLeft, typeRight) = typeReference.getPairTypes()
                mapList.map {
                    populate(it, 0, typeLeft) to populate(it, 1, typeRight)
                } as List<T>
            }
            typeReference.isParameterizedTriple() -> {
                val javaType = objectMapper.typeFactory.constructType(LinkedHashMap::class.java)
                val collectionType = objectMapper.typeFactory.constructCollectionType(ArrayList::class.java, javaType)
                val mapList = objectMapper.readValue(string, collectionType) as List<LinkedHashMap<String, Any?>>
                val (typeLeft, typeMiddle, typeRight) = typeReference.getTripleTypes()
                mapList.map {
                    Triple(populate(it, 0, typeLeft), populate(it, 1, typeMiddle), populate(it, 2, typeRight))
                } as List<T>
            }
            else -> {
                val javaType = objectMapper.typeFactory.constructType(typeReference.getType())
                val collectionType = objectMapper.typeFactory.constructCollectionType(ArrayList::class.java, javaType)
                objectMapper.readValue(string, collectionType) as List<T>
            }
        }
    }

    private fun populate(map: LinkedHashMap<String, Any?>, i: Int, type: KClass<*>): Any?{
        return if(Utils.isNotObject(type)){
            map.values.toList().getOrNull(i)?.let { Utils.tryConvert(it, type) }
        }else if(type.isData){
            KSQL.reflection.newDataInstance(type, map)
        }else if(type.isSubclassOf(Map::class)){
            map
        }else{
            val obj = KSQL.reflection.newInstance(type)
            KSQL.reflection.getFields(type).forEach { field ->
                if(map[field.name] != null){
                    field.set(obj, map[field.name])
                }
            }
            obj
        }
    }
}